/*
 * Copyright (c) 2023-2023 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.security.filter.username;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

/** 用户名密码登录 */
public class CustomUsernamePasswordAuthenticationFilter
    extends UsernamePasswordAuthenticationFilter {
  protected static ObjectMapper objectMapper = new ObjectMapper();
  private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER =
      new AntPathRequestMatcher("/login", "POST");

  /** 构造器 */
  public CustomUsernamePasswordAuthenticationFilter() {
    // https://github.com/spring-projects/spring-security/issues/13225
    setSecurityContextRepository(
        new DelegatingSecurityContextRepository(
            new RequestAttributeSecurityContextRepository(),
            new HttpSessionSecurityContextRepository()));
  }

  @Override
  public Authentication attemptAuthentication(
      HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    String username;
    String password;
    if (isJson(request)) {
      try {
        var read = objectMapper.reader().readValue(request.getInputStream(), Map.class);
        username = read.get(getUsernameParameter()) instanceof String u ? u : "";
        password = read.get(getPasswordParameter()) instanceof String p ? p : "";
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    } else {
      username = obtainUsername(request);
      password = obtainPassword(request);
    }
    username = (username != null) ? username.trim() : "";
    password = (password != null) ? password : "";
    UsernamePasswordAuthenticationToken authRequest =
        UsernamePasswordAuthenticationToken.unauthenticated(username, password);
    // Allow subclasses to set the "details" property
    setDetails(request, authRequest);
    return getAuthenticationManager().authenticate(authRequest);
  }

  protected boolean isJson(HttpServletRequest request) {
    return MediaType.APPLICATION_JSON.includes(
        MediaType.parseMediaType(request.getHeader(HttpHeaders.CONTENT_TYPE)));
  }
}
